#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "kernel.h"
#include "swis.h"

#include "dynarea.h"

#define DYNAMICAREA_READWRITE 0
#define DYNAMICAREA_READONLY  1

#define DYNAMICAREA_FIXEDBAR    1 << 7
#define DYNAMICAREA_USEHANDLERS 1 << 8
#define DYNAMICAREA_SHRINKABLE  1 << 9
#define DYNAMICAREA_SPARSE      1 << 10
#define DYNAMICAREA_BOUND       1 << 11

_kernel_oserror * DynamicArea_Create(char * name,
                                     int    maxsize,
                                     DynamicArea ** area)
{
  DynamicArea * newarea;
  _kernel_oserror * e;

  newarea = malloc(sizeof(DynamicArea));

  if (newarea)
  {
    e = _swix(OS_DynamicArea, _INR(0, 8) | _OUT(1) | _OUT(3),
              0,  // Create area
              -1, // Area number
              0,  // Initial size
              -1, // Area base
              DYNAMICAREA_READWRITE | DYNAMICAREA_FIXEDBAR,
              maxsize, // Maximum size
              NULL,    // handler routine
              -1,      // handler workspace
              name,    // area name
              &newarea->areano, // return area no
              &newarea->base);  // return base

    if (e)
    {
        free(newarea);
    }
    else
    {
        *area = newarea;
        newarea->maxSize     = maxsize;
        newarea->currentSize = 0;

//        printf("DynamicArea_Create(%s, %d, %x)\n", name, maxsize, area);
    }
  }
  else e = NULL; // FIX: error

  return e;
}

_kernel_oserror * DynamicArea_SetSize(DynamicArea * area,
                                      int           size)
{
    _kernel_oserror * e;
    int change = size - area->currentSize;

    if (size > area->maxSize)
    {
        e = NULL; // Too big
    }
    else if (size < 0)
    {
        e = NULL; // Try to make negative size
    }
    else
    {
//        printf("Change by %d : ", change);
        e = _swix(OS_ChangeDynamicArea, _INR(0, 1) | _OUT(1), area->areano, change, &change);
//        printf("%d\n", change);
        if (!e) area->currentSize += change;
    }

    return e;
}

_kernel_oserror * DynamicArea_Destroy(DynamicArea * area)
{
  _kernel_oserror * e;

  if (area)
  {
//    printf("DynamicArea_Destroy(%x)\n", area);
    e = _swix(OS_DynamicArea, _INR(0, 1), 1, area->areano);
    free(area);
  }
  else e = NULL; // FIX: error

  return e;
}
